﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Chess
{
    public class Board : IReadOnlyBoard
    {
        private readonly IReadOnlyList<PieceType> majorPieceInitialOrder =
            new PieceType[] { PieceType.Rook, PieceType.Knight, PieceType.Bishop, PieceType.Queen, PieceType.King, PieceType.Bishop, PieceType.Knight, PieceType.Rook };
        private readonly Tile[,] boardState = new Tile[Enum.GetValues(typeof(Column)).Length, Enum.GetValues(typeof(Row)).Length];
        private readonly ICollection<Move> history = new List<Move>();

        public Board()
        {
            for (Row row = Row.One; row <= Row.Eight; row++)
            {
                for (Column column = Column.A; column <= Column.H; column++)
                {
                    Piece piece = null;
                    switch (row)
                    {
                        case Row.One: { piece = new Piece(PieceColor.White, majorPieceInitialOrder[(int)column]); break; }
                        case Row.Two: { piece = new Piece(PieceColor.White, PieceType.Pawn); break; }
                        case Row.Seven: { piece = new Piece(PieceColor.Black, PieceType.Pawn); break; }
                        case Row.Eight: { piece = new Piece(PieceColor.Black, majorPieceInitialOrder[(int)column]); break; }
                    }
                    boardState[(int)column, (int)row] = new Tile(new Position(column, row), piece);
                }
            }
        }

        public IEnumerable<Move> History { get { return history; } }

        public IReadOnlyTile GetTile(Position position)
        {
            return GetTile(position.Column, position.Row);
        }
        public IReadOnlyTile GetTile(Column column, Row row)
        {
            return boardState[(int)column, (int)row];
        }
        public IEnumerable<IReadOnlyTile> TilesByColorOfPiece(PieceColor color)
        {
            // "Cast<Tile>" is called to flatten "boardState" to a simple IEnumerable<Tile>.
            return boardState.Cast<Tile>().Where(t => t.Piece != null && t.Piece.PieceColor == color);
        }
        public Status TryMove(Position from, Position to, PieceColor turnColor, PieceType promotionType)
        {
            Tile fromTile = (Tile)GetTile(from);
            Tile toTile = (Tile)GetTile(to);
            bool isValid = Rules.ValidateMove(this, fromTile, toTile, turnColor);
            Status status = Status.Invalid;
            if (isValid)
            {
                CommitMove(fromTile, toTile, turnColor, promotionType);
                status = Rules.BoardStatus(this, Piece.OppositeColor(turnColor));
            }
            return status;
        }
        private void CommitMove(Tile from, Tile to, PieceColor turnColor, PieceType promotionType)
        {
            Move move = new Move(this, from, to, promotionType);
            foreach (IReadOnlyTile tile in move.MutatedTiles)
            {
                ((Tile)GetTile(tile.Position)).Piece = tile.Piece;
            }
            history.Add(move);
        }
    }
}
